#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
错误处理中间件

这个模块包含全局错误处理中间件：
1. 捕获和处理未处理的异常
2. 返回统一的错误响应格式
3. 记录错误日志
4. 处理不同类型的异常
5. 提供开发和生产环境的不同错误信息

确保应用的稳定性和用户体验
提供详细的错误追踪信息

作者: FastAPI 学习项目
创建时间: 2024
"""

import traceback
import sys
from typing import Callable, Union
from fastapi import Request, HTTPException, status
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.exceptions import HTTPException as StarletteHTTPException
from sqlalchemy.exc import SQLAlchemyError, IntegrityError
from pydantic import ValidationError
from loguru import logger
import uuid
from datetime import datetime

from config import settings


class ErrorHandlerMiddleware(BaseHTTPMiddleware):
    """
    全局错误处理中间件
    
    捕获应用中的所有未处理异常，并返回统一格式的错误响应。
    同时记录详细的错误日志用于调试和监控。
    """
    
    def __init__(self, app, *, include_debug_info: bool = None):
        """
        初始化错误处理中间件
        
        Args:
            app: FastAPI 应用实例
            include_debug_info: 是否包含调试信息（默认根据环境判断）
        """
        super().__init__(app)
        self.include_debug_info = (
            include_debug_info 
            if include_debug_info is not None 
            else settings.DEBUG
        )
    
    async def dispatch(self, request: Request, call_next: Callable) -> JSONResponse:
        """
        处理请求并捕获异常
        
        Args:
            request: HTTP请求对象
            call_next: 下一个中间件或路由处理器
            
        Returns:
            JSONResponse: HTTP响应对象
        """
        try:
            response = await call_next(request)
            return response
            
        except Exception as e:
            return await self._handle_exception(request, e)
    
    async def _handle_exception(self, request: Request, exc: Exception) -> JSONResponse:
        """
        处理异常并返回错误响应
        
        Args:
            request: HTTP请求对象
            exc: 异常对象
            
        Returns:
            JSONResponse: 错误响应
        """
        # 生成错误ID
        error_id = str(uuid.uuid4())
        
        # 获取请求ID（如果存在）
        request_id = getattr(request.state, "request_id", None)
        
        # 根据异常类型处理
        if isinstance(exc, HTTPException):
            return await self._handle_http_exception(request, exc, error_id, request_id)
        elif isinstance(exc, StarletteHTTPException):
            return await self._handle_starlette_http_exception(request, exc, error_id, request_id)
        elif isinstance(exc, RequestValidationError):
            return await self._handle_validation_error(request, exc, error_id, request_id)
        elif isinstance(exc, ValidationError):
            return await self._handle_pydantic_validation_error(request, exc, error_id, request_id)
        elif isinstance(exc, SQLAlchemyError):
            return await self._handle_database_error(request, exc, error_id, request_id)
        else:
            return await self._handle_generic_exception(request, exc, error_id, request_id)
    
    async def _handle_http_exception(
        self, 
        request: Request, 
        exc: HTTPException, 
        error_id: str, 
        request_id: str
    ) -> JSONResponse:
        """
        处理 FastAPI HTTPException
        
        Args:
            request: HTTP请求对象
            exc: HTTPException 异常
            error_id: 错误ID
            request_id: 请求ID
            
        Returns:
            JSONResponse: 错误响应
        """
        # 记录日志
        logger.warning(
            f"HTTP异常: {exc.status_code} - {exc.detail}",
            extra={
                "error_id": error_id,
                "request_id": request_id,
                "status_code": exc.status_code,
                "detail": exc.detail,
                "path": request.url.path,
                "method": request.method
            }
        )
        
        # 构建响应
        error_response = {
            "error": {
                "code": exc.status_code,
                "message": exc.detail,
                "type": "http_exception",
                "error_id": error_id,
                "timestamp": datetime.utcnow().isoformat()
            }
        }
        
        if request_id:
            error_response["error"]["request_id"] = request_id
        
        return JSONResponse(
            status_code=exc.status_code,
            content=error_response,
            headers=getattr(exc, "headers", None)
        )
    
    async def _handle_starlette_http_exception(
        self, 
        request: Request, 
        exc: StarletteHTTPException, 
        error_id: str, 
        request_id: str
    ) -> JSONResponse:
        """
        处理 Starlette HTTPException
        
        Args:
            request: HTTP请求对象
            exc: StarletteHTTPException 异常
            error_id: 错误ID
            request_id: 请求ID
            
        Returns:
            JSONResponse: 错误响应
        """
        # 记录日志
        logger.warning(
            f"Starlette HTTP异常: {exc.status_code} - {exc.detail}",
            extra={
                "error_id": error_id,
                "request_id": request_id,
                "status_code": exc.status_code,
                "detail": exc.detail,
                "path": request.url.path,
                "method": request.method
            }
        )
        
        # 构建响应
        error_response = {
            "error": {
                "code": exc.status_code,
                "message": exc.detail,
                "type": "http_exception",
                "error_id": error_id,
                "timestamp": datetime.utcnow().isoformat()
            }
        }
        
        if request_id:
            error_response["error"]["request_id"] = request_id
        
        return JSONResponse(
            status_code=exc.status_code,
            content=error_response
        )
    
    async def _handle_validation_error(
        self, 
        request: Request, 
        exc: RequestValidationError, 
        error_id: str, 
        request_id: str
    ) -> JSONResponse:
        """
        处理请求验证错误
        
        Args:
            request: HTTP请求对象
            exc: RequestValidationError 异常
            error_id: 错误ID
            request_id: 请求ID
            
        Returns:
            JSONResponse: 错误响应
        """
        # 记录日志
        logger.warning(
            f"请求验证错误: {exc.errors()}",
            extra={
                "error_id": error_id,
                "request_id": request_id,
                "validation_errors": exc.errors(),
                "path": request.url.path,
                "method": request.method
            }
        )
        
        # 格式化验证错误
        formatted_errors = []
        for error in exc.errors():
            formatted_errors.append({
                "field": " -> ".join(str(loc) for loc in error["loc"]),
                "message": error["msg"],
                "type": error["type"]
            })
        
        # 构建响应
        error_response = {
            "error": {
                "code": status.HTTP_422_UNPROCESSABLE_ENTITY,
                "message": "请求数据验证失败",
                "type": "validation_error",
                "error_id": error_id,
                "timestamp": datetime.utcnow().isoformat(),
                "details": formatted_errors
            }
        }
        
        if request_id:
            error_response["error"]["request_id"] = request_id
        
        return JSONResponse(
            status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
            content=error_response
        )
    
    async def _handle_pydantic_validation_error(
        self, 
        request: Request, 
        exc: ValidationError, 
        error_id: str, 
        request_id: str
    ) -> JSONResponse:
        """
        处理 Pydantic 验证错误
        
        Args:
            request: HTTP请求对象
            exc: ValidationError 异常
            error_id: 错误ID
            request_id: 请求ID
            
        Returns:
            JSONResponse: 错误响应
        """
        # 记录日志
        logger.warning(
            f"Pydantic验证错误: {exc.errors()}",
            extra={
                "error_id": error_id,
                "request_id": request_id,
                "validation_errors": exc.errors(),
                "path": request.url.path,
                "method": request.method
            }
        )
        
        # 格式化验证错误
        formatted_errors = []
        for error in exc.errors():
            formatted_errors.append({
                "field": " -> ".join(str(loc) for loc in error["loc"]),
                "message": error["msg"],
                "type": error["type"]
            })
        
        # 构建响应
        error_response = {
            "error": {
                "code": status.HTTP_422_UNPROCESSABLE_ENTITY,
                "message": "数据验证失败",
                "type": "pydantic_validation_error",
                "error_id": error_id,
                "timestamp": datetime.utcnow().isoformat(),
                "details": formatted_errors
            }
        }
        
        if request_id:
            error_response["error"]["request_id"] = request_id
        
        return JSONResponse(
            status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
            content=error_response
        )
    
    async def _handle_database_error(
        self, 
        request: Request, 
        exc: SQLAlchemyError, 
        error_id: str, 
        request_id: str
    ) -> JSONResponse:
        """
        处理数据库错误
        
        Args:
            request: HTTP请求对象
            exc: SQLAlchemyError 异常
            error_id: 错误ID
            request_id: 请求ID
            
        Returns:
            JSONResponse: 错误响应
        """
        # 记录详细错误日志
        logger.error(
            f"数据库错误: {type(exc).__name__} - {str(exc)}",
            extra={
                "error_id": error_id,
                "request_id": request_id,
                "exception_type": type(exc).__name__,
                "exception_message": str(exc),
                "path": request.url.path,
                "method": request.method,
                "traceback": traceback.format_exc() if self.include_debug_info else None
            }
        )
        
        # 根据具体的数据库错误类型返回不同的消息
        if isinstance(exc, IntegrityError):
            message = "数据完整性约束违反，请检查输入数据"
            status_code = status.HTTP_409_CONFLICT
        else:
            message = "数据库操作失败，请稍后重试"
            status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
        
        # 构建响应
        error_response = {
            "error": {
                "code": status_code,
                "message": message,
                "type": "database_error",
                "error_id": error_id,
                "timestamp": datetime.utcnow().isoformat()
            }
        }
        
        if request_id:
            error_response["error"]["request_id"] = request_id
        
        # 在调试模式下包含更多信息
        if self.include_debug_info:
            error_response["error"]["debug_info"] = {
                "exception_type": type(exc).__name__,
                "exception_message": str(exc)
            }
        
        return JSONResponse(
            status_code=status_code,
            content=error_response
        )
    
    async def _handle_generic_exception(
        self, 
        request: Request, 
        exc: Exception, 
        error_id: str, 
        request_id: str
    ) -> JSONResponse:
        """
        处理通用异常
        
        Args:
            request: HTTP请求对象
            exc: Exception 异常
            error_id: 错误ID
            request_id: 请求ID
            
        Returns:
            JSONResponse: 错误响应
        """
        # 记录详细错误日志
        logger.error(
            f"未处理的异常: {type(exc).__name__} - {str(exc)}",
            extra={
                "error_id": error_id,
                "request_id": request_id,
                "exception_type": type(exc).__name__,
                "exception_message": str(exc),
                "path": request.url.path,
                "method": request.method,
                "traceback": traceback.format_exc()
            }
        )
        
        # 构建响应
        error_response = {
            "error": {
                "code": status.HTTP_500_INTERNAL_SERVER_ERROR,
                "message": "服务器内部错误，请稍后重试",
                "type": "internal_server_error",
                "error_id": error_id,
                "timestamp": datetime.utcnow().isoformat()
            }
        }
        
        if request_id:
            error_response["error"]["request_id"] = request_id
        
        # 在调试模式下包含更多信息
        if self.include_debug_info:
            error_response["error"]["debug_info"] = {
                "exception_type": type(exc).__name__,
                "exception_message": str(exc),
                "traceback": traceback.format_exc()
            }
        
        return JSONResponse(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            content=error_response
        )


class CustomExceptionHandler:
    """
    自定义异常处理器
    
    提供特定异常的处理方法。
    """
    
    @staticmethod
    def create_error_response(
        status_code: int,
        message: str,
        error_type: str = "error",
        details: dict = None,
        error_id: str = None
    ) -> JSONResponse:
        """
        创建标准错误响应
        
        Args:
            status_code: HTTP状态码
            message: 错误消息
            error_type: 错误类型
            details: 错误详情
            error_id: 错误ID
            
        Returns:
            JSONResponse: 错误响应
        """
        error_response = {
            "error": {
                "code": status_code,
                "message": message,
                "type": error_type,
                "timestamp": datetime.utcnow().isoformat()
            }
        }
        
        if error_id:
            error_response["error"]["error_id"] = error_id
        
        if details:
            error_response["error"]["details"] = details
        
        return JSONResponse(
            status_code=status_code,
            content=error_response
        )
    
    @staticmethod
    def handle_authentication_error() -> JSONResponse:
        """
        处理认证错误
        
        Returns:
            JSONResponse: 认证错误响应
        """
        return CustomExceptionHandler.create_error_response(
            status_code=status.HTTP_401_UNAUTHORIZED,
            message="认证失败，请重新登录",
            error_type="authentication_error"
        )
    
    @staticmethod
    def handle_authorization_error() -> JSONResponse:
        """
        处理授权错误
        
        Returns:
            JSONResponse: 授权错误响应
        """
        return CustomExceptionHandler.create_error_response(
            status_code=status.HTTP_403_FORBIDDEN,
            message="权限不足，无法访问此资源",
            error_type="authorization_error"
        )
    
    @staticmethod
    def handle_not_found_error(resource: str = "资源") -> JSONResponse:
        """
        处理资源未找到错误
        
        Args:
            resource: 资源名称
            
        Returns:
            JSONResponse: 未找到错误响应
        """
        return CustomExceptionHandler.create_error_response(
            status_code=status.HTTP_404_NOT_FOUND,
            message=f"{resource}不存在",
            error_type="not_found_error"
        )
    
    @staticmethod
    def handle_rate_limit_error(retry_after: int = None) -> JSONResponse:
        """
        处理限流错误
        
        Args:
            retry_after: 重试等待时间（秒）
            
        Returns:
            JSONResponse: 限流错误响应
        """
        headers = {}
        if retry_after:
            headers["Retry-After"] = str(retry_after)
        
        response = CustomExceptionHandler.create_error_response(
            status_code=status.HTTP_429_TOO_MANY_REQUESTS,
            message="请求过于频繁，请稍后重试",
            error_type="rate_limit_error",
            details={"retry_after": retry_after} if retry_after else None
        )
        
        # 添加头部
        for key, value in headers.items():
            response.headers[key] = value
        
        return response